home *** CD-ROM | disk | FTP | other *** search
/ IRIX 6.2 Applications 1996 May / SGI IRIX 6.2 Applications 1996 May.iso / dist / impr_dev.idb / usr / impressario / src / libprintui / PrintOptionPanel.c.z / PrintOptionPanel.c
C/C++ Source or Header  |  1996-05-06  |  49KB  |  1,681 lines

  1. /**************************************************************************
  2.  *
  3.  *           Copyright (c)    1993 Silicon Graphics, Inc.
  4.  *            All Rights Reserved
  5.  *
  6.  *       THIS    IS UNPUBLISHED PROPRIETARY SOURCE CODE OF SGI
  7.  *
  8.  * The copyright notice above does not evidence any actual of intended
  9.  * publication of such source code, and is an unpublished work by Silicon
  10.  * Graphics, Inc. This material contains CONFIDENTIAL INFORMATION that is
  11.  * the property of Silicon Graphics, Inc. Any use, duplication or
  12.  * disclosure not specifically authorized by Silicon Graphics is strictly
  13.  * prohibited.
  14.  *
  15.  * RESTRICTED RIGHTS LEGEND:
  16.  *
  17.  * Use, duplication or disclosure by the Government is subject to
  18.  * restrictions as set forth in subdivision (c)(1)(ii) of the Rights in
  19.  * Technical Data and Computer Software clause at DFARS 52.227-7013,
  20.  * and/or in similar or successor clauses in the FAR, DOD or NASA FAR
  21.  * Supplement. Unpublished - rights reserved under the Copyright Laws of
  22.  * the United States. Contractor is SILICON GRAPHICS, INC., 2011 N.
  23.  * Shoreline Blvd., Mountain View, CA 94039-7311
  24.  **************************************************************************
  25.  *
  26.  * File: PrintOptionPanel.c
  27.  *
  28.  * Description: Contains functions for handling the printer specific
  29.  *    option panel programs (a.k.a. gui_model files). These functions
  30.  *    are private to libprintui and should not be used for normal
  31.  *    application development. Normal access to the printer specific
  32.  *    options panels is obtained via the PrintBox widget.
  33.  *
  34.  *    These functions comprise the PrintOptionPanel portion of libprintui.
  35.  *    To access these function include PrintOptionPanel.h which is
  36.  *    installed in /usr/include/Sgm. All functions in this portion of
  37.  *    libprintui are prefixed with PuiOptionPanel.
  38.  *
  39.  *    The functions handle creation, display, tear down and window
  40.  *    management of a printer option panel program. The normal life
  41.  *    cycle is:
  42.  *
  43.  *    1. Instantiate a printer option panel program using
  44.  *       PuiOptionPanelCreate. The function will test if there is
  45.  *       an option panel for the specified printer but you can do
  46.  *       this explicitly with a call to PuiOptionPanelAlive.
  47.  *
  48.  *    2. Run the options program with a call to PuiOptionPanelExec.
  49.  *       This will execute the program and display the window.
  50.  *
  51.  *    3. You can explicitly kill the optin panel with a call to
  52.  *       PuiOptionPanelKill.
  53.  *
  54.  *    4. When you no longer need the option panel, call
  55.  *       PuiOptionPanelDestroy. This will implicitly do a
  56.  *       PuiOptionPanelKill if the option panel is running.
  57.  *
  58.  *    The option panel communicates with your app via callbacks. Using
  59.  *    the function PuiOptionPanelSetCallback for each of the following
  60.  *    callbacks you may register a single function and spcify client
  61.  *    data. Look at the definition of PuiOptionPanelCallbackStruct
  62.  *    in the header file for the structure fields that are valid for
  63.  *    each callback. The callbacks are:
  64.  *
  65.  *    PuiNoptionPanelErrorCallback
  66.  *        Called when an error has occurred. If an error has occurred
  67.  *        during a PuiOptionPanelExec that causes the option
  68.  *        panel to not run you will get the error callback followed
  69.  *        but a death callback to indicate the option panel could not
  70.  *        be run. Note that under certain error conditions you
  71.  *        will get a death callback with no corresponding error
  72.  *        callback.
  73.  *
  74.  *    PuiNoptionPanelMapCallback
  75.  *        Called when the option panel maps on the screen.
  76.  *
  77.  *    PuiNoptionPanelDeathCallback
  78.  *        Called when the option panel program terminates normally or
  79.  *        abnormally.
  80.  *
  81.  *    PuiNoptionPanelDataCallback
  82.  *        Called when an option string is avaiable. Do not free
  83.  *        the string passed in the callback. Copy the string if
  84.  *        you wish to preserve it.
  85.  *
  86.  **************************************************************************/
  87.  
  88.  
  89. #ident "$Revision: 1.10 $"
  90.  
  91.  
  92. #include <stdio.h>
  93. #include <stdlib.h>
  94. #include <unistd.h>
  95. #include <assert.h>
  96. #include <string.h>
  97. #include <sys/types.h>
  98. #include <sys/wait.h>
  99. #include <sys/uio.h>
  100. #include <sys/stat.h>
  101. #include <signal.h>
  102. #include <limits.h>
  103. #include <syslog.h>
  104. #include <pwd.h>
  105. #include <errno.h>
  106. #include <signal.h>
  107. #include <fcntl.h>
  108. #include <X11/Xproto.h>
  109. #include <X11/Intrinsic.h>
  110. #include <X11/StringDefs.h>
  111. #include <Sgm/PrintOptionPanel.h>
  112. #include "PuiI.h"
  113. #include "RootWin.h"
  114.  
  115.  
  116. /* Printer option panel info */
  117.  
  118. #define OPTION_PANEL_DIR        "/var/spool/lp/gui_interface"
  119. #define SPOOL_APP_DEFAULTS_DIR    "/var/spool/lp/app-defaults"
  120.  
  121.  
  122. /* Callback macros */
  123.  
  124. #define InitCallback(cb)            \
  125.         cb.reason = PuiCR_OPTION_NONE;    \
  126.         cb.errorCode = 0;        \
  127.         cb.wid = None;            \
  128.         cb.options = NULL;
  129.  
  130.  
  131. /* File scope variables */
  132.  
  133. static Widget rootWin;            /* Root window widget for events */
  134. static char *username;            /* User name based on uid */
  135.  
  136.  
  137. /* Local functions */
  138.  
  139. static char* FindOptionPanel(const char *printer);
  140. static char** BuildArgList(PuiOptionPanel *panel,
  141.                     const PuiOptionPanelArgs *args);
  142. static Window FindTopWindow(Display *disp, Window win);
  143. static Window SearchChildWindows(Display *disp, Window win);
  144. static Boolean WindowIsOptionPanel(PuiOptionPanel *panel, Window win);
  145. static int GetWMState(Display *disp, Window win);
  146. static void HandleError(PuiOptionPanel *panel, int errCode);
  147. static void HandleMappingCB(Widget root, XtPointer clientData, XEvent *event,
  148.                         Boolean *contDispatch);
  149. static void HandleParentCB(Widget root, XtPointer clientData, XEvent *event,
  150.                         Boolean *contDispatch);
  151. static void HandleInputCB(XtPointer clientData, int *fdp, XtInputId *inputID);
  152. static void HandleDeath(PuiOptionPanel *panel);
  153. static void HandleData(PuiOptionPanel *panel, char *optionStr);
  154. static void CallDeathCallback(PuiOptionPanel *panel);
  155. static void RaisePanel(PuiOptionPanel *panel);
  156. static Widget FindShell(Widget w);
  157. static void SetXSearchPath(char *printerName);
  158.  
  159.  
  160. /**************************************************************************
  161.  *
  162.  * Function: _PuiOptionPanelExists
  163.  *
  164.  * Description: Tests whether there is a printer specific option panel
  165.  *    program for the specified printer.
  166.  *
  167.  * Parameters:
  168.  *    printer (I) - name of the printer for which an option panel is
  169.  *            desired.
  170.  *
  171.  * Return: True if option panel exists and False if not.
  172.  *
  173.  **************************************************************************/
  174.  
  175. Boolean _PuiOptionPanelExists(const char *printer)
  176. {
  177.     register char *ptr;
  178.  
  179.     assert(printer != NULL);
  180.     if ((ptr = FindOptionPanel(printer)) == NULL)
  181.     return False;
  182.     XtFree(ptr);
  183.     return True;
  184. }
  185.  
  186.  
  187. /**************************************************************************
  188.  *
  189.  * Function: _PuiOptionPanelAlive
  190.  *
  191.  * Description: Tests whether the specified option panel program is
  192.  *    executing.
  193.  *
  194.  * Parameters: 
  195.  *    panel (I) - option panel structure
  196.  *
  197.  * Return: True if option panel program is executing and False if not.
  198.  *
  199.  **************************************************************************/
  200.  
  201. Boolean _PuiOptionPanelAlive(PuiOptionPanel *panel)
  202. {
  203.     assert(panel != NULL);
  204.     return ((panel->pid == 0 || (kill(panel->pid, 0) < 0))? False: True);
  205. }
  206.  
  207.  
  208. /**************************************************************************
  209.  *
  210.  * Function: _PuiOptionPanelCreate
  211.  *
  212.  * Description: Creates an instance of a printer specific options panel.
  213.  *    The option panel data structure is allocated and initialized.
  214.  *    The option panel program is not actually run until a
  215.  *    PuiOptionPanelExec is performed.
  216.  *
  217.  * Parameters: 
  218.  *    parent (I) - widget ID of the "parent" of the option panel. The
  219.  *            parent must be a descendent of the Core widget
  220.  *            class.
  221.  *    printerName (I) - name of the printer whose option panel is to
  222.  *            be launched.
  223.  *    followParent (I) - True = follow parent's mapping changes. If the
  224.  *            parent unmap, the option panel window, if up,
  225.  *            will be withdrawn. When the parent maps, the
  226.  *            option panel will be mapped again. Note that
  227.  *            this only works if we can detect the option panel
  228.  *            window mapping. If we cannot detect the mapping
  229.  *            we can;t get the panel WID so we cannot effect
  230.  *            its window.
  231.  *
  232.  * Return: If the printer option panel program exists, a pointer to an
  233.  *    option panel structure is returned. If the panel program could
  234.  *    not be found NULL will be returned. Note that the storage for the
  235.  *    option panel structure is allocated by the function. It is the
  236.  *    caller's responsibility to free this storage when no longer needed
  237.  *    using a call to the _PuiOptionPanelDestroy function.
  238.  *
  239.  **************************************************************************/
  240.  
  241. PuiOptionPanel* _PuiOptionPanelCreate(Widget parent, const char *printerName,
  242.                         Boolean followParent)
  243. {
  244.     char *optionProg;
  245.     PuiOptionPanel *optionPanel;
  246.     struct passwd *user_pw;
  247.     Widget shell;
  248.  
  249.     /*
  250.      * Sanity check our inputs
  251.      */
  252.     assert(parent != NULL);
  253.     assert(printerName != NULL);
  254.  
  255.     /*
  256.      * Determine if a printer option panel program exists for the
  257.      * specified printer.
  258.      */
  259.     if ((optionProg = FindOptionPanel(printerName)) == NULL)
  260.     return NULL;
  261.  
  262.     /*
  263.      * Find the Shell on which the parent widget lives. This shell will be
  264.      * used as the parent for the RootWin widget as well as for catching
  265.      * map/unmap events if we are to follow the parent.
  266.      */
  267.     shell = FindShell(parent);
  268.  
  269.     /*
  270.      * If this is the first time we are here, determine
  271.      * the user's login name
  272.      */
  273.     if (!username) {
  274.         if ((user_pw = getpwuid(getuid())) == NULL) {
  275.         char buf[30];
  276.         sprintf(buf, "uid%u", getuid());
  277.         username = XtNewString(buf);
  278.         } else
  279.         username = XtNewString(user_pw->pw_name);
  280.     }
  281.  
  282.     /*
  283.      * Allocate storage for the option panel structure
  284.      */
  285.     optionPanel = (PuiOptionPanel*)XtMalloc(sizeof(PuiOptionPanel));
  286.  
  287.     /*
  288.      * Initialize the option panel structure
  289.      */
  290.     optionPanel->name = XtNewString(printerName);
  291.     optionPanel->pathname = optionProg;
  292.     optionPanel->parent = parent;
  293.     optionPanel->pid = 0;
  294.     optionPanel->dataFd = -1;
  295.     optionPanel->ptyFd = -1;
  296.     optionPanel->inputId = 0;
  297.     optionPanel->wid = None;
  298.     optionPanel->wantWID = False;
  299.     optionPanel->followParent = followParent;
  300.     optionPanel->shell = shell;
  301.     optionPanel->errorCallback = NULL;
  302.     optionPanel->mapCallback = NULL;
  303.     optionPanel->deathCallback = NULL;
  304.     optionPanel->dataCallback = NULL;
  305.     optionPanel->errorClientData = NULL;
  306.     optionPanel->mapClientData = NULL;
  307.     optionPanel->deathClientData = NULL;
  308.     optionPanel->dataClientData = NULL;
  309.  
  310.     return optionPanel;
  311. }
  312.  
  313.  
  314. /**************************************************************************
  315.  *
  316.  * Function: _PuiOptionPanelDestroy
  317.  *
  318.  * Description: Terminates the option panel program if it is running,
  319.  *    and deallocates the option panel structure storage thereby
  320.  *    destroying the option panel handle. The handle should not be
  321.  *    referenced after a call to this function.
  322.  *
  323.  * Parameters: 
  324.  *    panel (I) - pointer to an option panel structure
  325.  *
  326.  * Return: none
  327.  *
  328.  **************************************************************************/
  329.  
  330. void _PuiOptionPanelDestroy(PuiOptionPanel *panel)
  331. {
  332.     /*
  333.      * Sanity checks
  334.      */
  335.     if (!panel)
  336.     return;
  337.  
  338.     /*
  339.      * Kill the option panel if it is alive
  340.      */
  341.     _PuiOptionPanelKill(panel, False);
  342.  
  343.     /*
  344.      * Deallocate the storage for the structure
  345.      */
  346.     if (panel->name) XtFree(panel->name);
  347.     if (panel->pathname) XtFree(panel->pathname);
  348.     XtFree((char*)panel);
  349. }
  350.  
  351.  
  352. /**************************************************************************
  353.  *
  354.  * Function: _PuiOptionPanelSetCallback
  355.  *
  356.  * Description: Registers the specified function as the callback function
  357.  *    for the specified condition. Currently, only a single callback
  358.  *    function can be registered per condition.
  359.  *
  360.  * Parameters: 
  361.  *    panel (I) - option panel structure
  362.  *    callbackName (I) - name of callback (e.g. optionPanelErrorCallback)
  363.  *    callbackProc (I) - function to call. Setting this to NULL
  364.  *                indicates no interest in the callback.
  365.  *    clientData (I) - client specific information
  366.  *
  367.  * Return: none
  368.  *
  369.  **************************************************************************/
  370.  
  371. void _PuiOptionPanelSetCallback(PuiOptionPanel *panel,
  372.                 PuiOptionPanelCallbackName callbackName,
  373.                 PuiOptionPanelCallbackProc callbackProc,
  374.                 XtPointer clientData)
  375. {
  376.     /*
  377.      * Sanity checks
  378.      */
  379.     assert(panel != NULL);
  380.  
  381.     /*
  382.      * Assign the callback
  383.      */
  384.     switch (callbackName) {
  385.     case PuiNoptionPanelErrorCallback:
  386.         panel->errorCallback = callbackProc;
  387.         panel->errorClientData = clientData;
  388.         break;
  389.     case PuiNoptionPanelMapCallback: 
  390.         panel->mapCallback = callbackProc;
  391.         panel->mapClientData = clientData;
  392.         break;
  393.     case PuiNoptionPanelDeathCallback:
  394.         panel->deathCallback = callbackProc;
  395.         panel->deathClientData = clientData;
  396.         break;
  397.     case PuiNoptionPanelDataCallback:
  398.         panel->dataCallback = callbackProc;
  399.         panel->dataClientData = clientData;
  400.         break;
  401.     }
  402. }
  403.  
  404.  
  405. /**************************************************************************
  406.  *
  407.  * Function: _PuiOptionPanelExec
  408.  *
  409.  * Description: Launches a printer options panel program. The program will
  410.  *    not be launched if it is already running. To catch any errors
  411.  *    that may occurr during the launching process an error callback
  412.  *    should be registered.
  413.  *
  414.  * Parameters: 
  415.  *    panel (I) - optionspanel structure
  416.  *    args (I) - option panel command line arguments structure. If
  417.  *            NULL is specified a default set of command line
  418.  *            arguments will be used. The default command line
  419.  *            arguments structure is:
  420.  *
  421.  *            userName = username from getpwuid of getuid
  422.  *            filenames = NULL
  423.  *            options = NULL
  424.  *            clientXtOptions = NULL
  425.  *            numClientXtOptions = 0
  426.  *
  427.  *            This results in a command line for the option panel
  428.  *            that looks like:
  429.  *
  430.  *            <printername> <username> ""
  431.  *
  432.  *            If args is specified but the username field is
  433.  *            NULL, the default username will be used.
  434.  *
  435.  * Return: None. To catach errors, register an error callback.
  436.  *
  437.  **************************************************************************/
  438.  
  439. void _PuiOptionPanelExec(PuiOptionPanel *panel, const PuiOptionPanelArgs *args)
  440. {
  441.     char **argv, *ptyName;
  442.     int status, dataPipe[2];
  443.     pid_t retv, pid1, pid2;
  444.     struct sigaction sact, origSact;
  445.     Boolean errorCond;
  446.  
  447.     /*
  448.      * Sanity checks
  449.      */
  450.     assert(panel != NULL);
  451.  
  452.     /*
  453.      * You will notice that throughout the program we create a root window
  454.      * widget before we use it. This is in case the root window widget was
  455.      * destroyed because its parent was destroyed. If the root window widget
  456.      * if stiff valid, the _PuiCreateRootWin function simply returns the
  457.      * widget ID of the already existing root window widget. This is not a
  458.      * leak just a requirement for working with the root window widget.
  459.      */
  460.     rootWin = _PuiCreateRootWin(panel->parent, "_puiRootWin", NULL, 0);
  461.  
  462.     /*
  463.      * Test whether the option panel is already running. If it is
  464.      * running, deiconify it and raise it to the top of the window stack.
  465.      * We also generate a call on the mapping callback as if the window
  466.      * had just mapped. That way, if someone wants to clear a busy
  467.      * cursor on the map event they can.
  468.      */
  469.     if (_PuiOptionPanelAlive(panel)) {
  470.     RaisePanel(panel);
  471.         if (panel->mapCallback) {
  472.             PuiOptionPanelCallbackStruct cb;
  473.             InitCallback(cb);
  474.             cb.reason = PuiCR_OPTION_MAP;
  475.             cb.wid = panel->wid;
  476.             (panel->mapCallback)(panel, panel->mapClientData, &cb);
  477.     }
  478.     return;
  479.     }
  480.  
  481.     /*
  482.      * Build the command line argument list
  483.      */
  484.     argv = BuildArgList(panel, args);
  485.  
  486.     /*
  487.      * Put an event handler on the root window to catch
  488.      * the option panel's map event.
  489.      */
  490.     XtAddEventHandler(rootWin, SubstructureNotifyMask, False,
  491.                     HandleMappingCB, (XtPointer)panel);
  492.     XFlush(XtDisplay(rootWin));
  493.  
  494.     /*
  495.      * Open a pipe to read from the option panel's stdout
  496.      */
  497.     if (pipe(dataPipe) < 0) {
  498.     HandleError(panel, errno);
  499.         XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  500.                         HandleMappingCB, (XtPointer)panel);
  501.         XtFree((char*)argv);
  502.     CallDeathCallback(panel);
  503.     return;
  504.     }
  505.  
  506.     /*
  507.      * Set SIGCLD to SIG_DFL.
  508.      *
  509.      * We are going to fork a child and that child will fork a child
  510.      * (sounds biblical). We default SIGCLD for the first child so that
  511.      * app signal handlers do not get called when the first child terminates.
  512.      * Using POSIX handling, any app child that dies will have its SIGCLD
  513.      * reasserted when we un-default the signal mask. Meanwhile the
  514.      * second child will be forked off in SIGCLD ignore mode so that it
  515.      * never generates a SIGCLD and never leaves a zombie.
  516.      */
  517.     sact.sa_handler = SIG_DFL;
  518.     sigemptyset(&sact.sa_mask);
  519.     sact.sa_flags = 0;
  520.     sigaction(SIGCLD, &sact, &origSact);
  521.  
  522.     /*
  523.      * Now for some psuedo-terminal hocus pocus. The problem is that we
  524.      * want the second child (i.e. the options panel program) to terminate
  525.      * (i.e. be sent a SIGHUP) when the parent (its grandparent) dies for
  526.      * any reason. We cannot use prctl(PR_TERMCHILD) because of the death
  527.      * of the intervening first child. So we use a pty instead. The parent
  528.      * holds the master end and the second child holds the slave end. When
  529.      * the master end closes due to death of the parent, the second child
  530.      * is sent a SIGHUP. This works for kill -9 and all other cases.
  531.      */
  532.     ptyName = _getpty(&panel->ptyFd, O_RDWR, 0600, 0);
  533.     if (ptyName == NULL) {
  534.     HandleError(panel, errno);
  535.         sigaction(SIGCLD, &origSact, NULL);
  536.     close(dataPipe[0]);
  537.     close(dataPipe[1]);
  538.     panel->ptyFd = -1;
  539.     XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  540.                         HandleMappingCB, (XtPointer)panel);
  541.     XtFree((char*)argv);
  542.     CallDeathCallback(panel);
  543.     return;
  544.     }
  545.  
  546.     /*
  547.      * Indicate to the event handler that we want the WID of the option
  548.      * panel program.
  549.      */
  550.     panel->wantWID = True;
  551.  
  552.     /*
  553.      * Fork the first child
  554.      */
  555.     if ((pid1 = fork()) < 0) {
  556.     HandleError(panel, errno);
  557.         sigaction(SIGCLD, &origSact, NULL);
  558.     close(dataPipe[0]);
  559.     close(dataPipe[1]);
  560.         panel->wantWID = False;
  561.     XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  562.                         HandleMappingCB, (XtPointer)panel);
  563.     XtFree((char*)argv);
  564.     CallDeathCallback(panel);
  565.     return;
  566.     }
  567.  
  568.     /* First Child ----------------------------------------------------*/
  569.  
  570.     if (pid1 == 0) {
  571.     register int i;
  572.     pid_t pid;
  573.     int fd;
  574.  
  575.     /*
  576.      * Set the SIGCLD for the second child to SIG_IGN so that
  577.      * we do not get SIGCLD and we do not get zombies.
  578.      */
  579.         sact.sa_handler = SIG_IGN;
  580.         sigemptyset(&sact.sa_mask);
  581.         sact.sa_flags = 0;
  582.         sigaction(SIGCLD, &sact, NULL);
  583.  
  584.     /*
  585.      * Fork the second child. The first child writes the pid
  586.      * of the second child to the pipe. The parent will read
  587.      * the pid from the pipe.
  588.      */
  589.     if ((pid = fork()) < 0) {
  590.         write(dataPipe[1], &pid, sizeof(pid_t));
  591.         exit(errno);
  592.     }
  593.     if (pid > 0) {
  594.         write(dataPipe[1], &pid, sizeof(pid_t));
  595.         exit(0);
  596.     }
  597.  
  598.         /* Second Child ------------------------------------------------*/
  599.  
  600.     /*
  601.      * The second child must become a process group leader and loose
  602.      * its controlling terminal so that we can use the pty as the
  603.      * controlling terminal.
  604.      */
  605.     setsid();
  606.  
  607.     /*
  608.      * Ensure that SIGHUP is DFL
  609.      */
  610.         sact.sa_handler = SIG_DFL;
  611.         sigemptyset(&sact.sa_mask);
  612.         sact.sa_flags = 0;
  613.         sigaction(SIGHUP, &sact, NULL);
  614.  
  615.     /*
  616.      * Connect our stdout to the write end of the pipe
  617.      */
  618.     if (dataPipe[1] != 1) {
  619.         close(1);
  620.         fcntl(dataPipe[1], F_DUPFD, 1);
  621.     }
  622.  
  623.     /*
  624.      * Close all file descriptors except 0, 1 and 2.
  625.      */
  626.     for (i = getdtablesize() - 1; i > 2; i--)
  627.         close(i);
  628.  
  629.     /*
  630.      * We open the slave end of the psuedo-terminal so that
  631.      * it becomes our controlling terminal and we get a HUP
  632.      * when the parent dies.
  633.      */
  634.     if (open(ptyName, O_RDWR) < 0) {
  635.         syslog(LOG_ERR, "Could not open pty for option panel %s: %m",
  636.                 panel->pathname);
  637.         exit(errno);
  638.     }
  639.  
  640.     /*
  641.      * Make sure we look for app-defaults in the spooling
  642.      * subdirectory first before looking in the normal place.
  643.      * This is because network printers will have the app-defaults
  644.      * file for their option panel copied to a spooler app-default
  645.      * directory by lputil.
  646.      */
  647.     SetXSearchPath(argv[0]);
  648.  
  649.     /*
  650.      * Believe it or not, it is now time to run the
  651.      * option panel program. If we get past exec, an
  652.      * error has occurred and we exit with errno.
  653.      */
  654.     execv(panel->pathname, argv);
  655.  
  656.     syslog(LOG_ERR, "Could not exec option panel %s: %m", panel->pathname);
  657.     exit(errno);
  658.     }
  659.  
  660.     /* Parent ---------------------------------------------------------*/
  661.  
  662.     /*
  663.      * Close the write end of parent's pipe
  664.      */
  665.     close(dataPipe[1]);
  666.  
  667.     /*
  668.      * Read the second child's pid from the pipe
  669.      */
  670.     read(dataPipe[0], &pid2, sizeof(pid_t));
  671.  
  672.     /*
  673.      * Wait for the first child to terminate. Its exit status is
  674.      * the either 0 if the second child was successfully forked, or
  675.      * it is the value of errno if an error occurred while forking the
  676.      * second child.
  677.      */
  678.     while (((retv = waitpid(pid1, &status, 0)) < 0) && oserror() == EINTR)
  679.     ;
  680.  
  681.     /*
  682.      * If we had an error executing the second child handle it
  683.      */
  684.     errorCond = False;
  685.     if (retv < 0) {
  686.         errorCond = True;
  687.     HandleError(panel, errno);
  688.     } else if (pid2 < 0 || (WIFEXITED(status) && WEXITSTATUS(status) != 0)) {
  689.         errorCond = True;
  690.     HandleError(panel, WEXITSTATUS(status));
  691.     }
  692.     if (errorCond) {
  693.         sigaction(SIGCLD, &origSact, NULL);
  694.     close(dataPipe[0]);
  695.     close(panel->ptyFd);
  696.     panel->ptyFd = -1;
  697.         panel->wantWID = False;
  698.     XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  699.                         HandleMappingCB, (XtPointer)panel);
  700.     XtFree((char*)argv);
  701.     CallDeathCallback(panel);
  702.     return;
  703.     }
  704.  
  705.     /*
  706.      * Restore the original signal handler
  707.      */
  708.     sigaction(SIGCLD, &origSact, NULL);
  709.  
  710.     /*
  711.      * Free the command line arguments list
  712.      */
  713.     XtFree((char*)argv);
  714.  
  715.     /*
  716.      * Save the option panel's pid and fd
  717.      */
  718.     panel->pid = pid2;
  719.     panel->dataFd = dataPipe[0];
  720.  
  721.     /*
  722.      * Listen for data on the pipe
  723.      */
  724.     panel->inputId = XtAppAddInput(XtWidgetToApplicationContext(panel->shell),
  725.                 panel->dataFd, (XtPointer)XtInputReadMask,
  726.                 HandleInputCB, (XtPointer)panel);
  727. }
  728.  
  729.  
  730. /**************************************************************************
  731.  *
  732.  * Function: _PuiOptionPanelKill
  733.  *
  734.  * Description: Kills an option panel. If the panel is not running, this
  735.  *    function silently returns. If the panel is running we send it
  736.  *    a SIGTERM.
  737.  *
  738.  * Parameters: 
  739.  *    panel (I) - option panel structure
  740.  *    notify (I) - True = call death callback if panel is running
  741.  *
  742.  * Return: none
  743.  *
  744.  **************************************************************************/
  745.  
  746. void _PuiOptionPanelKill(PuiOptionPanel *panel, Boolean notify)
  747. {
  748.     pid_t pid;
  749.  
  750.     /*
  751.      * Sanity checks
  752.      */
  753.     assert(panel != NULL);
  754.  
  755.     /*
  756.      * First test whether the panel is alive. If it is not we simply
  757.      * return.
  758.      */
  759.     if (!_PuiOptionPanelAlive(panel))
  760.     return;
  761.  
  762.     /*
  763.      * Remember the pid so that we can clean up and then do the kill
  764.      */
  765.     pid = panel->pid;
  766.     HandleDeath(panel);
  767.  
  768.     /*
  769.      * We will issue the kill to remove the panel
  770.      */
  771.     kill(pid, SIGTERM);
  772.  
  773.     /*
  774.      * Close the pty
  775.      */
  776.     close(panel->ptyFd);
  777.     panel->ptyFd = -1;
  778.  
  779.     /*
  780.      * Call the death callback if desired
  781.      */
  782.     if (notify)
  783.         CallDeathCallback(panel);
  784. }
  785.  
  786.  
  787. /*
  788.  ============================================================================
  789.             LOCAL FUNCTIONS
  790.  ============================================================================
  791.  */
  792.  
  793.  
  794. /**************************************************************************
  795.  *
  796.  * Function: FindOptionPanel
  797.  *
  798.  * Description: Looks for a printer option panel.
  799.  *
  800.  * Parameters: 
  801.  *    printer (I) - name of the printer whose option panel is desired.
  802.  *
  803.  * Return: If the option panel exists and is executable a pointer to the
  804.  *    full pathname of the option panel program is returned. It is the
  805.  *    caller's responsibility to free the storage for the returned
  806.  *    pathname. If the option panel is not found, NULL is returned.
  807.  *
  808.  **************************************************************************/
  809.  
  810. static char* FindOptionPanel(const char *printer)
  811. {
  812.     char *optionPathname = (char*)XtMalloc(strlen(printer) +
  813.                     strlen(OPTION_PANEL_DIR) + 20);
  814.     
  815.     /*
  816.      * First look in the ELF subdirectory for the option panel
  817.      * and then in the primary option panel directory.
  818.      */
  819.     sprintf(optionPathname, "%s/ELF/%s.gui", OPTION_PANEL_DIR, printer);
  820.     if (access(optionPathname, R_OK | X_OK) < 0) {
  821. /*
  822.  
  823.  Comment this out becuase this dir contains COFF and 6.2+ will not
  824.  work with COFF.
  825.  
  826.         sprintf(optionPathname, "%s/%s.gui", OPTION_PANEL_DIR, printer);
  827.         if (access(optionPathname, R_OK | X_OK) < 0) {
  828. */
  829.         XtFree(optionPathname);
  830.         optionPathname = NULL;
  831. /*
  832.     }
  833. */
  834.     }
  835.  
  836.     return optionPathname;
  837. }
  838.  
  839.  
  840. /**************************************************************************
  841.  *
  842.  * Function: BuildArgList
  843.  *
  844.  * Description: Constructs an array of command line arguments for the
  845.  *    option's panel program exec. The array is NULL terminated.
  846.  *
  847.  * Parameters: 
  848.  *    panel (I) - option panel structure
  849.  *    args (I) - option panel arguments structure
  850.  *
  851.  * Return: Pointer to NULL terminated array of command line arguments.
  852.  *    It is the caller's job to free the array. Note that only the
  853.  *    array itself needs to be freed, the strings themselves are not
  854.  *    allocated here.
  855.  *
  856.  **************************************************************************/
  857.  
  858. static char** BuildArgList(PuiOptionPanel *panel,
  859.                     const PuiOptionPanelArgs *args)
  860. {
  861.     char **argv;
  862.     register int n = 0, i;
  863.  
  864.     /*
  865.      * Allocate enough room in the list for the fixed options plus
  866.      * any Xt client options that are specified.
  867.      */
  868.     if (args)
  869.         argv = (char**)XtCalloc(10 + args->numClientXtOptions, sizeof(char*));
  870.     else
  871.         argv = (char**)XtCalloc(10, sizeof(char*));
  872.  
  873.     /*
  874.      * Build the array of arguments
  875.      */
  876.     argv[n++] = panel->name;
  877.     argv[n++] = (args && args->userName)? args->userName: username;
  878.     argv[n++] = (args && args->filenames)? args->filenames: "";
  879.     if (args) {
  880.         argv[n] = (args->options)? args->options: ""; n++;
  881.     for (i = 0; i < args->numClientXtOptions; i++)
  882.         argv[n++] = (args->clientXtOptions[i])?
  883.                         args->clientXtOptions[i]: "";
  884.     }
  885.  
  886.     return argv;
  887. }
  888.  
  889.  
  890. /**************************************************************************
  891.  *
  892.  * Function: HandleError
  893.  *
  894.  * Description: Handles a execution error by calling the user's error
  895.  *    callback if one is registered.
  896.  *
  897.  * Parameters: 
  898.  *    panel (I) - option panel structure
  899.  *    errCode (I) - error code to report in callback
  900.  *
  901.  * Return: none
  902.  *
  903.  **************************************************************************/
  904.  
  905. static void HandleError(PuiOptionPanel *panel, int errCode)
  906. {
  907.     PuiOptionPanelCallbackStruct cb;
  908.  
  909.     /*
  910.      * If there is no callback function don't do anything
  911.      */
  912.     if (!panel->errorCallback)
  913.     return;
  914.  
  915.     /*
  916.      * Initialize the structure
  917.      */
  918.     InitCallback(cb);
  919.  
  920.     /*
  921.      * Set appropriate fields and call the callback function
  922.      */
  923.     cb.reason = PuiCR_OPTION_ERROR;
  924.     cb.errorCode = errCode;
  925.     (panel->errorCallback)(panel, panel->errorClientData, &cb);
  926. }
  927.  
  928.  
  929. /**************************************************************************
  930.  *
  931.  * Function: HandleParentCB
  932.  *
  933.  * Description: Event handler for structure notify events from the parent
  934.  *    window. The purpose of this function is to allow the option panel
  935.  *    to follow the mapping state of its parent. If the parent unmaps,
  936.  *    the option panel window is withdrawn. If the parent maps, the
  937.  *    option panel window is mapped. This event handler is only installed
  938.  *    when the followParent parameter is True and an option panel is
  939.  *    running.
  940.  *
  941.  * Parameters: 
  942.  *    w (I) - widget on which the event occured
  943.  *    clientData (I) - a pointer to the option panel structure
  944.  *    event (I) - the X event that triggered the callback
  945.  *    contDispatch (I) - always set to True so that the event propagates.
  946.  *
  947.  * Return: none
  948.  *
  949.  **************************************************************************/
  950.  
  951. static void HandleParentCB(Widget w, XtPointer clientData, XEvent *event,
  952.                         Boolean *contDispatch)
  953. {
  954.     PuiOptionPanel *panel = (PuiOptionPanel*)clientData;
  955.     XWMHints *hints;
  956.     int x, y, ourState;
  957.     Window root;
  958.     unsigned int width, height, bwidth, depth;
  959.  
  960.     /*
  961.      * Make sure we have a WID and that the event is of a type
  962.      * we are interested in. Also make sure that the window effected
  963.      * is the parent window.
  964.      */
  965.     if (panel->wid == None ||
  966.         (event->type != MapNotify && event->type != UnmapNotify) ||
  967.         (XtWindow(w) != XtWindow(panel->shell)))
  968.     return;
  969.  
  970.     /*
  971.      * First get our window state. Assume normal state if can't get it.
  972.      */
  973.     if ((ourState = GetWMState(XtDisplay(w), panel->wid)) < 0)
  974.     ourState = NormalState;
  975.  
  976.     /*
  977.      * Now if the parent has unmapped and we are not already withdrawn,
  978.      * save the current state of the option panel window and withdraw it.
  979.      */
  980.     if (event->type == UnmapNotify && ourState != WithdrawnState) {
  981.     if ((hints = XGetWMHints(XtDisplay(w), panel->wid)) == NULL)
  982.         panel->stateHints.flags = StateHint;
  983.     else {
  984.         panel->stateHints = *hints;
  985.         XFree((char*)hints);
  986.     }
  987.     panel->stateHints.initial_state = ourState;
  988.     if (XGetGeometry(XtDisplay(w), panel->wid, &root, &x, &y,
  989.                 &width, &height, &bwidth, &depth) == 0)
  990.         panel->sizeHints.flags = 0;
  991.     else {
  992.         panel->sizeHints.flags =  USPosition | USSize;
  993.         panel->sizeHints.x = x;
  994.         panel->sizeHints.y = y;
  995.         panel->sizeHints.width = width;
  996.         panel->sizeHints.height = height;
  997.     }
  998.     XWithdrawWindow(XtDisplay(w), panel->wid, DefaultScreen(XtDisplay(w)));
  999.     }
  1000.     /*
  1001.      * Similarly if the parent has mapped and we are withdrawn, bring
  1002.      * our option panel window back to its original state and position
  1003.      * on the screen.
  1004.      */
  1005.     else if (event->type == MapNotify && ourState == WithdrawnState) {
  1006.     XSetWMHints(XtDisplay(w), panel->wid, &panel->stateHints);
  1007.     XSetWMNormalHints(XtDisplay(w), panel->wid, &panel->sizeHints);
  1008.     XMapWindow(XtDisplay(w), panel->wid);
  1009.     }
  1010. }
  1011.  
  1012.  
  1013. /**************************************************************************
  1014.  *
  1015.  * Function: HandleMappingCB
  1016.  *
  1017.  * Description: Event handler for substructure notify events from
  1018.  *    the root window. The sole purpose of this function is to
  1019.  *    detect the initial mapping of the optionpanel window on the
  1020.  *    root window. As soon as this event is detected, this event handler
  1021.  *    removes itself.
  1022.  *
  1023.  * Parameters: 
  1024.  *    root (I) - widget on which the event occured
  1025.  *    clientData (I) - a pointer to the option panel structure
  1026.  *    event (I) - the X event that triggered the callback
  1027.  *    contDispatch (I) - always set to True so that the event propagates.
  1028.  *
  1029.  * Return: none
  1030.  *
  1031.  **************************************************************************/
  1032.  
  1033. static void HandleMappingCB(Widget root, XtPointer clientData, XEvent *event,
  1034.                         Boolean *contDispatch)
  1035. {
  1036.     PuiOptionPanel *panel = (PuiOptionPanel*)clientData;
  1037.     PuiOptionPanelCallbackStruct cb;
  1038.     Window topWindow;
  1039.  
  1040.     /*
  1041.      * Make sure we have a valid root window widget
  1042.      */
  1043.     rootWin = _PuiCreateRootWin(panel->parent, "_puiRootWin", NULL, 0);
  1044.  
  1045.     /*
  1046.      * Do nothing if we aren't looking for an option panel WID
  1047.      * or if the event is not a window mapping event.
  1048.      */
  1049.     if (!panel->wantWID || event->type != MapNotify)
  1050.     return;
  1051.  
  1052.     /*
  1053.      * If the window being mapped is override redirect we are
  1054.      * not interested. The WM puts up an override redirect window
  1055.      * for apps that don't have explicit placement.
  1056.      */
  1057.     if (event->xmap.override_redirect)
  1058.     return;
  1059.  
  1060.     /*
  1061.      * We want to get the WID of the top level window. A reparenting
  1062.      * window manager will return the WID of its reparented window in
  1063.      * the event structure. What we want is the top level window below
  1064.      * that. In any case the ICCCM define the top level window as that
  1065.      * which is not override redirect and has the WM_STATE property set
  1066.      * on it. If the property is not set, the top level is considered
  1067.      * the window we pass in.
  1068.      */
  1069.     topWindow = FindTopWindow(XtDisplay(root), event->xmap.window);
  1070.  
  1071.     /*
  1072.      * Now that we have the top level window we want to see if it is
  1073.      * the option panel program . We do this by first looking at the
  1074.      * instance name of the program and comparing it against the current
  1075.      * printer name. If this fails we try the wWM name and finally the
  1076.      * icon name.
  1077.      */
  1078.     if (WindowIsOptionPanel(panel, topWindow) == False)
  1079.     return;
  1080.  
  1081.     /*
  1082.      * If we go here, we have an option panel program. First thing
  1083.      * is to show no more interest in getting WIDs. Then we remove
  1084.      * the event handler and set the panel structures WID variable.
  1085.      */
  1086.     panel->wantWID = False;
  1087.     XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  1088.                         HandleMappingCB, (XtPointer)panel);
  1089.     panel->wid = topWindow;
  1090.  
  1091.     /*
  1092.      * If we have been asked to follow the parent mapping. In order to
  1093.      * do this we must install an event handler on the shell that the
  1094.      * parent widget lives on. Only the shell will get the map/unmap
  1095.      * event.
  1096.      */
  1097.     if (panel->followParent) {
  1098.         XtAddEventHandler(panel->shell, StructureNotifyMask, False,
  1099.                     HandleParentCB, (XtPointer)panel);
  1100.         XFlush(XtDisplay(rootWin));
  1101.     }
  1102.  
  1103.     /*
  1104.      * Now call the option panel mapping event callback, if any
  1105.      */
  1106.     if (!panel->mapCallback)
  1107.     return;
  1108.     InitCallback(cb);
  1109.     cb.reason = PuiCR_OPTION_MAP;
  1110.     cb.wid = panel->wid;
  1111.     (panel->mapCallback)(panel, panel->mapClientData, &cb);
  1112. }
  1113.  
  1114.  
  1115. /**************************************************************************
  1116.  *
  1117.  * Function: GetWMState
  1118.  *
  1119.  * Description: If the WM_STATE atom is defined on the specified top level
  1120.  *    window, return its value.
  1121.  *
  1122.  * Parameters: 
  1123.  *    disp (I) - X display
  1124.  *    win (I) - top level window to read WM_STATE from
  1125.  *
  1126.  * Return: 
  1127.  *    -2 = WM_STATE atom not interned.
  1128.  *    -1 = WM_STATE atom not found on top level window.
  1129.  *    0 = WithdrawnState
  1130.  *    1 = NormalState
  1131.  *    2 = [obsolete]
  1132.  *    3 = IconicState
  1133.  *    4 = [obsolete]
  1134.  *
  1135.  **************************************************************************/
  1136.  
  1137. static int GetWMState(Display *disp, Window win)
  1138. {
  1139.     static Atom WM_STATE = None;
  1140.     Atom actualType = None;
  1141.     int actualFormat;
  1142.     unsigned long nitems, bytesAfter;
  1143.     unsigned char *propReturn;
  1144.  
  1145.     /*
  1146.      * First attempt to get the WM_STATE atom.
  1147.      */
  1148.     if (WM_STATE == None)
  1149.         WM_STATE = XInternAtom(disp, "WM_STATE", True);
  1150.     if (WM_STATE == None)
  1151.     return -2;
  1152.  
  1153.     /*
  1154.      * Next we attempt to get the WM_STATE property off the window.
  1155.      * We consider the property set on the window if the function
  1156.      * returns Success and actualType != None.
  1157.      */
  1158.     if (XGetWindowProperty(disp, win, WM_STATE, 0, 1, False,
  1159.         AnyPropertyType, &actualType, &actualFormat,
  1160.         &nitems, &bytesAfter, &propReturn) == Success &&
  1161.         actualType != None) {
  1162.     int state = (int)*((CARD32*)propReturn);
  1163.     XFree((char*)propReturn);
  1164.     return state;
  1165.     }
  1166.  
  1167.     /*
  1168.      * Never found WM_STATE.
  1169.      */
  1170.     return -1;
  1171. }
  1172.  
  1173.  
  1174. /**************************************************************************
  1175.  *
  1176.  * Function: FindTopWindow
  1177.  *
  1178.  * Description: Finds the top level window in the window heirarchy rooted
  1179.  *    at the specified window. The ICCCM defines the top level window
  1180.  *    as that window that is not override redirect and has the WM_STATE
  1181.  *    propery. If the WM_STATE property is not defined or found then
  1182.  *    the window passed in is considred the top level window.
  1183.  *
  1184.  * Parameters: 
  1185.  *    disp (I) - X display
  1186.  *    win (I) - window from which to start the search for the top
  1187.  *        level window.
  1188.  *
  1189.  * Return: Top level window.
  1190.  *
  1191.  **************************************************************************/
  1192.  
  1193. static Window FindTopWindow(Display *disp, Window win)
  1194. {
  1195.     int state;
  1196.     Window retWin;
  1197.  
  1198.     /*
  1199.      * Next we attempt to get the WM_STATE property off the window.
  1200.      * If we cannot even intern the WM_STATE atom forget even searching.
  1201.      * IF the WM_STATE property is set we found the top.
  1202.      */
  1203.     if ((state = GetWMState(disp, win)) == -2)
  1204.     return win;
  1205.     if (state != -1)
  1206.     return win;
  1207.  
  1208.     /*
  1209.      * If we got here, the WM_STATE property is not set on the window
  1210.      * we passed in so we do a breadth first search of the window
  1211.      * hierarchy.
  1212.      */
  1213.     if ((retWin = SearchChildWindows(disp, win)) != None)
  1214.     return retWin;
  1215.  
  1216.     /*
  1217.      * Never found WM_STATE so per ICCCM we assume the original window
  1218.      * if the top level window.
  1219.      */
  1220.     return win;
  1221. }
  1222.  
  1223.  
  1224. /**************************************************************************
  1225.  *
  1226.  * Function: SearchChildWindows
  1227.  *
  1228.  * Description: Performs a breadth first recursive search on the window
  1229.  *    hierarchy looking for the specified property.
  1230.  *
  1231.  * Parameters: 
  1232.  *    disp (I) - X display
  1233.  *    win (I) - window to root search
  1234.  *
  1235.  * Return: Window ID of window containing specified atom or None if atom
  1236.  *    not found on any window.
  1237.  *
  1238.  **************************************************************************/
  1239.  
  1240. static Window SearchChildWindows(Display *disp, Window win)
  1241. {
  1242.     Window root, parent, *children;
  1243.     Window retWin = None;
  1244.     unsigned int numChildren;
  1245.     register int i;
  1246.  
  1247.     /*
  1248.      * Get the window heirarchy for the current window
  1249.      */
  1250.     if (!XQueryTree(disp, win, &root, &parent, &children, &numChildren))
  1251.     return None;
  1252.  
  1253.     /*
  1254.      * Search the children for the Atom property
  1255.      */
  1256.     for (i = 0; retWin == None && i < numChildren; i++) {
  1257.     if (GetWMState(disp, children[i]) != -1)
  1258.         retWin = children[i];
  1259.     }
  1260.  
  1261.     /*
  1262.      * Did not find it on any children at this level so we
  1263.      * must recurse down a level.
  1264.      */
  1265.     for (i = 0; retWin == None && i < numChildren; i++)
  1266.     retWin = SearchChildWindows(disp, children[i]);
  1267.  
  1268.     /*
  1269.      * Free the sotrage for the child window array
  1270.      */
  1271.     if (children)
  1272.     XFree((char*)children);
  1273.  
  1274.     return retWin;
  1275. }
  1276.  
  1277.  
  1278. /**************************************************************************
  1279.  *
  1280.  * Function: WindowIsOptionPanel
  1281.  *
  1282.  * Description: Determines if the specified top level window is an
  1283.  *     option panel program. We do this by first looking at the
  1284.  *    instance name of the program and comparing it against the current
  1285.  *    printer name. If this fails we try the wWM name and finally the
  1286.  *    icon name.
  1287.  *
  1288.  * Parameters: 
  1289.  *    panel (I) - option panel structure
  1290.  *    win (I) - top level window to test
  1291.  *
  1292.  * Return: True if window is an option panel program, False if not.
  1293.  *
  1294.  **************************************************************************/
  1295.  
  1296. static Boolean WindowIsOptionPanel(PuiOptionPanel *panel, Window win)
  1297. {
  1298.     XClassHint classHints;
  1299.     XTextProperty textProp;
  1300.  
  1301.     /*
  1302.      * Make sure we have a valid root window widget
  1303.      */
  1304.     rootWin = _PuiCreateRootWin(panel->parent, "_puiRootWin", NULL, 0);
  1305.  
  1306.     /*
  1307.      * First try to get the class hints
  1308.      */
  1309.     if (XGetClassHint(XtDisplay(rootWin), win, &classHints)) {
  1310.     int retv = strcmp(classHints.res_name, panel->name);
  1311.     XFree(classHints.res_name);
  1312.     XFree(classHints.res_class);
  1313.     if (!retv) return True;
  1314.     }
  1315.  
  1316.     /*
  1317.      * If that failed, try looking at the WM name
  1318.      */
  1319.     if (XGetWMName(XtDisplay(rootWin), win, &textProp)) {
  1320.     int retv = strcmp(textProp.value, panel->name);
  1321.     XFree(textProp.value);
  1322.     if (!retv) return True;
  1323.     }
  1324.  
  1325.     /*
  1326.      * And finally we try the icon name
  1327.      */
  1328.     if (XGetWMIconName(XtDisplay(rootWin), win, &textProp)) {
  1329.     int retv = strcmp(textProp.value, panel->name);
  1330.     XFree(textProp.value);
  1331.     if (!retv) return True;
  1332.     }
  1333.  
  1334.     return False;
  1335. }
  1336.  
  1337.  
  1338. /**************************************************************************
  1339.  *
  1340.  * Function: HandleInputCB
  1341.  *
  1342.  * Description: Called whenever the select in the event loop returns with
  1343.  *    input pending on the option panel pipe. Note that this routine is
  1344.  *    called both when data is pending and when the option panel dies.
  1345.  *    the option panel death case is identified by a read on the pipe
  1346.  *    returning zero.
  1347.  *
  1348.  * Parameters: 
  1349.  *    clientData (I) - option panel structure
  1350.  *    fdp (I) - pointer to pipe fd
  1351.  *    inputID (I) - Xt ID for the input handler
  1352.  *
  1353.  * Return: none
  1354.  *
  1355.  **************************************************************************/
  1356.  
  1357. static void HandleInputCB(XtPointer clientData, int *fdp, XtInputId *inputID)
  1358. {
  1359.     PuiOptionPanel *panel = (PuiOptionPanel*)clientData;
  1360.     char inputBuf[PIPE_BUF];
  1361.     int retv;
  1362.  
  1363.     /*
  1364.      * Make sure we have a valid root window widget
  1365.      */
  1366.     rootWin = _PuiCreateRootWin(panel->parent, "_puiRootWin", NULL, 0);
  1367.  
  1368.     /*
  1369.      * Remove the event handler. Note that it may not be installed
  1370.      * but it does not hurt to remove it directly.
  1371.      */
  1372.     XtRemoveEventHandler(rootWin, SubstructureNotifyMask, False,
  1373.                         HandleMappingCB, (XtPointer)panel);
  1374.  
  1375.     /*
  1376.      * We read the pipe. If 0 is returned, the write end of the pipe
  1377.      * has been closed and we assume the child has died. Otherwise we
  1378.      * assume we have an options string.
  1379.      */
  1380.     retv = read(*fdp, inputBuf, PIPE_BUF);
  1381.  
  1382.     /*
  1383.      * If an error occurred while reading we issue a callback
  1384.      */
  1385.     if (retv < 0) {
  1386.     HandleError(panel, errno);
  1387.     return;
  1388.     }
  1389.  
  1390.     /*
  1391.      * If we read 0 we call the death handler. The close the
  1392.      * pty and call the death callback.
  1393.      */
  1394.     if (retv == 0) {
  1395.     HandleDeath(panel);
  1396.         close(panel->ptyFd);
  1397.         panel->ptyFd = -1;
  1398.     CallDeathCallback(panel);
  1399.     return;
  1400.     }
  1401.  
  1402.     /*
  1403.      * If we actually read something, make sure it is
  1404.      * NULL terminated and call the data handler
  1405.      */
  1406.     inputBuf[(retv == PIPE_BUF)? retv - 1: retv] = '\0';
  1407.     HandleData(panel, inputBuf);
  1408. }
  1409.  
  1410.  
  1411. /**************************************************************************
  1412.  *
  1413.  * Function: HandleDeath
  1414.  *
  1415.  * Description: Handles the death of an option panel program by removing
  1416.  *    any event handlers, input handlers, closing file descriptors and
  1417.  *    putting the optionpanel structure in an initialized state.
  1418.  *
  1419.  * Parameters: 
  1420.  *    panel (I) - option panel structure
  1421.  *
  1422.  * Return: none
  1423.  *
  1424.  **************************************************************************/
  1425.  
  1426. static void HandleDeath(PuiOptionPanel *panel)
  1427. {
  1428.     /*
  1429.      * Make sure we have a valid root window widget
  1430.      */
  1431.     rootWin = _PuiCreateRootWin(panel->parent, "_puiRootWin", NULL, 0);
  1432.  
  1433.     /*
  1434.      * The first thing to do is remove the input handler and
  1435.      * close the pipe.
  1436.      */
  1437.     if (panel->inputId) {
  1438.         XtRemoveInput(panel->inputId);
  1439.         XFlush(XtDisplay(rootWin));
  1440.     }
  1441.     if (panel->dataFd != -1)
  1442.         close(panel->dataFd);
  1443.  
  1444.     /*
  1445.      * Remove the parent mapping event handler if we have been asked
  1446.      * to follow the parent.
  1447.      */
  1448.     if (panel->followParent)
  1449.         XtRemoveEventHandler(panel->shell, StructureNotifyMask, False,
  1450.                         HandleParentCB, (XtPointer)panel);
  1451.  
  1452.     /*
  1453.      * Initialize structure variables
  1454.      */
  1455.     panel->pid = 0;
  1456.     panel->wid = None;
  1457.     panel->wantWID = False;
  1458.     panel->inputId = 0;
  1459.     panel->dataFd = -1;
  1460. }
  1461.  
  1462.  
  1463. /**************************************************************************
  1464.  *
  1465.  * Function: CallDeathCallback
  1466.  *
  1467.  * Description: Calls the death callback function if any is registered.
  1468.  *
  1469.  * Parameters: 
  1470.  *    panel (I) - option panel structure
  1471.  *
  1472.  * Return: none
  1473.  *
  1474.  **************************************************************************/
  1475.  
  1476. static void CallDeathCallback(PuiOptionPanel *panel)
  1477. {
  1478.     PuiOptionPanelCallbackStruct cb;
  1479.  
  1480.     if (!panel->deathCallback)
  1481.     return;
  1482.     InitCallback(cb);
  1483.     cb.reason = PuiCR_OPTION_DEATH;
  1484.     (panel->deathCallback)(panel, panel->deathClientData, &cb);
  1485. }
  1486.  
  1487.  
  1488. /**************************************************************************
  1489.  *
  1490.  * Function: HandleData
  1491.  *
  1492.  * Description: Sends the options string out on the data callback.
  1493.  *
  1494.  * Parameters: 
  1495.  *    panel (I) - option panel structure
  1496.  *    optionStr (I) - NULL terminated option string
  1497.  *
  1498.  * Return: none
  1499.  *
  1500.  **************************************************************************/
  1501.  
  1502. static void HandleData(PuiOptionPanel *panel, char *optionStr)
  1503. {
  1504.     PuiOptionPanelCallbackStruct cb;
  1505.     char *cptr;
  1506.  
  1507.     /*
  1508.      * Terminate the string at the first newline
  1509.      */
  1510.     if ((cptr = strchr(optionStr, '\n')) != NULL)
  1511.     *cptr = '\0';
  1512.  
  1513.     /*
  1514.      * Call the data callback, if any
  1515.      */
  1516.     if (!panel->dataCallback)
  1517.     return;
  1518.     InitCallback(cb);
  1519.     cb.reason = PuiCR_OPTION_DATA;
  1520.     cb.options = optionStr;
  1521.     (panel->dataCallback)(panel, panel->dataClientData, &cb);
  1522. }
  1523.  
  1524.  
  1525. /**************************************************************************
  1526.  *
  1527.  * Function: RaisePanel
  1528.  *
  1529.  * Description: Forces the option panel window into the normal state and
  1530.  *    raises it to the top of the window stack.
  1531.  *
  1532.  * Parameters: 
  1533.  *    panel (I) - option panel structure
  1534.  *
  1535.  * Return: none
  1536.  *
  1537.  **************************************************************************/
  1538.  
  1539. static void RaisePanel(PuiOptionPanel *panel)
  1540. {
  1541.     static Atom WM_CHANGE_STATE = None;
  1542.     XClientMessageEvent event;
  1543.     int state;
  1544.  
  1545.     /*
  1546.      * Ensure we have a window to play with
  1547.      */
  1548.     if (panel->wid == None)
  1549.     return;
  1550.  
  1551.     /*
  1552.      * Get our winodw's state
  1553.      */
  1554.     state = GetWMState(XtDisplay(panel->shell), panel->wid);
  1555.  
  1556.     /*
  1557.      * We send the window manager a request to deiconify our 
  1558.      * window if it is not in the normal state.
  1559.      */
  1560.     if (state != NormalState) {
  1561.     /*
  1562.      * First get the atom if we have never done that
  1563.      */
  1564.     if (WM_CHANGE_STATE == None)
  1565.         WM_CHANGE_STATE = XInternAtom(XtDisplay(panel->shell),
  1566.                         "WM_CHANGE_STATE", False);
  1567.     if (WM_CHANGE_STATE == None)
  1568.         return;
  1569.  
  1570.     /*
  1571.      * Then send the deiconify event
  1572.      */
  1573.     event.type = ClientMessage;
  1574.     event.window = panel->wid;
  1575.     event.message_type = WM_CHANGE_STATE;
  1576.     event.format = 32;
  1577.     event.data.l[0] = NormalState;
  1578.     XSendEvent(XtDisplay(panel->shell),
  1579.         RootWindowOfScreen(XtScreen(panel->shell)),
  1580.         False, SubstructureRedirectMask | SubstructureNotifyMask,
  1581.         (XEvent*)&event);
  1582.     }
  1583.  
  1584.     /*
  1585.      * Make sure the window is at the top of the stack
  1586.      */
  1587.     XRaiseWindow(XtDisplay(panel->shell), panel->wid);
  1588. }
  1589.  
  1590.  
  1591. /**************************************************************************
  1592.  *
  1593.  * Function: FindShell
  1594.  *
  1595.  * Description: Traverses back up through the widget hierarchy of the
  1596.  *    specified widget looking for a subclass of Shell.
  1597.  *
  1598.  * Parameters: 
  1599.  *    w (I) - base widget from which to start upward parent traverse
  1600.  *
  1601.  * Return: Shell widget ancestor of the specified widget.
  1602.  *
  1603.  **************************************************************************/
  1604.  
  1605. static Widget FindShell(Widget w)
  1606. {
  1607.     Widget shell = w;
  1608.  
  1609.     while (shell) {
  1610.     if (XtIsShell(shell))
  1611.         break;
  1612.     shell = XtParent(shell);
  1613.     }
  1614.  
  1615.     return ((shell)? shell: w);
  1616. }
  1617.  
  1618.  
  1619. /**************************************************************************
  1620.  *
  1621.  * Function: SetXSearchPath
  1622.  *
  1623.  * Description: Gets the current setting of XUSERFILESEARCHPATH and prepends
  1624.  *    on it the value of SPOOL_APP_DEFAULTS_DIR plus the printer name
  1625.  *    and then sets that as the new X search path.
  1626.  *
  1627.  * Parameters:
  1628.  *    printerName (I) - name of the printer
  1629.  *
  1630.  * Return: none
  1631.  *
  1632.  **************************************************************************/
  1633.  
  1634. static void SetXSearchPath(char *printerName)
  1635. {
  1636.     char *origPath, *newPath;
  1637.     char *varStr = "XUSERFILESEARCHPATH=";
  1638.     char *spoolStr;
  1639.  
  1640.     /*
  1641.      * Form the spooling app-defaults path name
  1642.      */
  1643.     spoolStr = (char*)XtMalloc(strlen(SPOOL_APP_DEFAULTS_DIR) +
  1644.                                                 strlen(printerName) + 10);
  1645.     sprintf(spoolStr, "%s/%s/%%N", SPOOL_APP_DEFAULTS_DIR, printerName);
  1646.  
  1647.     /*
  1648.      * Get the original path, if any
  1649.      */
  1650.     origPath = getenv("XUSERFILESEARCHPATH");
  1651.  
  1652.     /*
  1653.      * Allocate storage for the new path
  1654.      */
  1655.     newPath = (char*)XtMalloc(strlen(varStr) +
  1656.                 ((origPath)? strlen(origPath): 0) +
  1657.                 strlen(spoolStr) + 5);
  1658.  
  1659.     /*
  1660.      * For the new path
  1661.      */
  1662.     strcpy(newPath, varStr);
  1663.     strcat(newPath, spoolStr);
  1664.     if (origPath) {
  1665.     strcat(newPath, ":");
  1666.     strcat(newPath, origPath);
  1667.     }
  1668.  
  1669.     /*
  1670.      * Set new search path
  1671.      */
  1672.     putenv(newPath);
  1673.  
  1674.     /*
  1675.      * Free storage for spool string. Do not free new env string
  1676.      * since putenv references it.
  1677.      */
  1678.     XtFree(spoolStr);
  1679. }
  1680.  
  1681.